package ca.seia.comp6521.a2.io;

import gnu.trove.list.array.TIntArrayList;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import ca.seia.comp6521.a2.hashstorage.HashBucket;
import ca.seia.comp6521.a2.hashstorage.HashBucketController;
import ca.seia.comp6521.a2.itemsetalgorithms.BitVector;
import ca.seia.comp6521.a2.itemsetalgorithms.CandidateGenerator;
import ca.seia.comp6521.a2.itemsetalgorithms.PassIIResult;
import ca.seia.comp6521.a2.itemsetalgorithms.PassIResult;
import ca.seia.comp6521.a2.itemsetalgorithms.PassNResult;
import ca.seia.comp6521.a2.itemsetstorage.CharTriplet;
import ca.seia.comp6521.a2.itemsetstorage.IntItemCountStorage;
import ca.seia.comp6521.a2.itemsetstorage.IntItems;
import ca.seia.comp6521.a2.itemsetstorage.IntTriplet;
import ca.seia.comp6521.a2.itemsetstorage.PairStorage;

public class DataFileReader {

	public static final String FILE_DELIMITER = " ";
	
	private String absolutefilePath;
	private BufferedReader reader;
	private int frequentSingleItemCapacity = 2000;
	
	public DataFileReader(String absolutefilePath) throws FileNotFoundException {
		this.absolutefilePath = absolutefilePath;
		this.reader = createBufferedReader(absolutefilePath);
	}
	
	private BufferedReader createBufferedReader(String filePath) throws FileNotFoundException {
		return new BufferedReader(new FileReader(this.absolutefilePath));
	}

	public DataFileReader(String absolutefilePath, int frequentSingleItemCapacity) throws FileNotFoundException {
		this(absolutefilePath);
		this.frequentSingleItemCapacity = frequentSingleItemCapacity;
	}
	
	/*
	 * Method to execute pass1
	 */
	public FileReadResult readTransactions() {
		int transactions = 0;
		int fileTransactionItemCount = 0;
		int transactionItemCount= 0;
		int biggestTransactionItemCount = 0;
		String line = null;
		int[] frequentSingleItemCount = new int[frequentSingleItemCapacity];
		long startTime = System.currentTimeMillis();
		try {
			while ((line = reader.readLine()) != null) {
				String[] items = line.split(FILE_DELIMITER);
				for(int itemIndex = 0 ; itemIndex < items.length ; itemIndex++) {
					transactionItemCount++;
					frequentSingleItemCount[Integer.parseInt(items[itemIndex])]++;
				}
//				StringTokenizer tokenizer = new StringTokenizer(line, " ");
//				while(tokenizer.hasMoreTokens()) {
//					transactionItemCount++;
//					frequentSingleItemCount[Integer.parseInt(tokenizer.nextToken())]++;
//				}
				biggestTransactionItemCount = Math.max(biggestTransactionItemCount, transactionItemCount);
				fileTransactionItemCount += transactionItemCount;
				transactions++;
				transactionItemCount = 0;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		long executionTime = System.currentTimeMillis() - startTime;
//		System.out.println("phase1 execution time = " + executionTime);
		return new FileReadResult(transactions, biggestTransactionItemCount, fileTransactionItemCount/transactions, frequentSingleItemCount);
	}
	
	public FileReadResult readTransactionsPCY(HashBucket... hashBuckets) {
		int transactions = 0;
		int fileTransactionItemCount = 0;
		int transactionItemCount= 0;
		int biggestTransactionItemCount = 0;
		String line = null;
		int[] frequentSingleItemCount = new int[frequentSingleItemCapacity];
		long startTime = System.currentTimeMillis();
		try {
			while ((line = reader.readLine()) != null) {
				String[] items = line.split(FILE_DELIMITER);
				for(int itemIndex = 0 ; itemIndex < items.length ; itemIndex++) {
					transactionItemCount++;
					int item1 = Integer.parseInt(items[itemIndex]);
					frequentSingleItemCount[item1]++;
					//generate all the pair combinations for this line and add to hash bucket
					for (int item2Index = itemIndex+1 ; item2Index < items.length ; item2Index++) {
						int item2 = Integer.parseInt(items[item2Index]);
						
						//Multhash
						for(HashBucket hashBucket:hashBuckets) {
							hashBucket.addCountToBucket(item1, item2);
						}
					}
				}
//				StringTokenizer tokenizer = new StringTokenizer(line, " ");
//				while(tokenizer.hasMoreTokens()) {
//					transactionItemCount++;
//					frequentSingleItemCount[Integer.parseInt(tokenizer.nextToken())]++;
//				}
				biggestTransactionItemCount = Math.max(biggestTransactionItemCount, transactionItemCount);
				fileTransactionItemCount += transactionItemCount;
				transactions++;
				transactionItemCount = 0;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		long executionTime = System.currentTimeMillis() - startTime;
//		System.out.println("phase1 execution time = " + executionTime);
		return new FileReadResult(transactions, biggestTransactionItemCount, fileTransactionItemCount/transactions, frequentSingleItemCount);
	}
	
	/**
	 *  
	 * @param passIResult
	 * @param pairStorage
	 * @throws FileNotFoundException
	 */
	public void readTransactions(PassIResult passIResult,PairStorage pairStorage) throws FileNotFoundException {
		this.reader = createBufferedReader(getAbsolutefilePath());
		String line = null;
		String[] items = null;
		TIntArrayList frequentItems; 
		BitVector bitVector = passIResult.getBitVector();
		int transactionItemSize = 0;
		int transactionCount = 0;
		try {
			while ((line = reader.readLine()) != null) {
//				items = pattern.split(line);
				items = line.split(FILE_DELIMITER);
//				System.out.println(Arrays.toString(items));
				frequentItems = new TIntArrayList();
				transactionCount ++;
//				System.out.println("transaction line = " + transactionCount);
				transactionItemSize = items.length;
//				System.out.println("Number of items in transaction = " + transactionItemSize);
//				System.out.println("bitVector Biggest Freuqent item = " + bitVector.getBiggestFrequentItem());
//				System.out.println("bitVector is " + bitVector);
				
				for(int item = 0; item < transactionItemSize ; item++) {
					int itemValue = Integer.parseInt(items[item]);
					if (itemValue > bitVector.getBiggestFrequentItem()) {
						break;
					}
					if(bitVector.isFrequentItem(itemValue)) {
						frequentItems.add(itemValue);
					}
//					System.out.println("Frequent items for Line " + frequentItems);
//					System.out.println("frequent items count " + frequentItems.size());
				}
				
				int frequentItemsLength = frequentItems.size();
				for (int item1 = 0 ; item1 < frequentItemsLength ; item1++) {
					for (int item2 = item1+1 ; item2 < frequentItemsLength ; item2++) {
						int item1Value = frequentItems.get(item1);
						int item2Value = frequentItems.get(item2);
//						System.out.println("adding pair ( " + item1Value +" , " + item2Value + " ) ");
						pairStorage.addCount(item1Value, item2Value);
					}
				}
			}
//			System.out.println(pairStorage);
//			System.out.println("finished reading PassII transactions");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 *  
	 * @param passIResult
	 * @param pairStorage
	 * @throws FileNotFoundException
	 */
	public void readTransactionsPassIIPCY(PassIResult passIResult,PairStorage pairStorage) throws FileNotFoundException {
		//Pattern pattern = Pattern.compile(FILE_DELIMITER);
		this.reader = createBufferedReader(getAbsolutefilePath());
		String line = null;
		String[] items = null;
		TIntArrayList frequentItems; 
		BitVector bitVector = passIResult.getBitVector();
		HashBucketController bucketController = new HashBucketController(passIResult.getHashBitVectors());
		int transactionItemSize = 0;
		int transactionCount = 0;
		try {
			while ((line = reader.readLine()) != null) {
//				items = pattern.split(line);
				items = line.split(FILE_DELIMITER);
				frequentItems = new TIntArrayList();
				transactionCount ++;
//				System.out.println("transaction line = " + transactionCount);
				transactionItemSize = items.length;
				//System.out.println("Number of items in transaction = " + transactionItemSize);
				//System.out.println("bitVector Biggest Freuqent item = " + bitVector.getBiggestFrequentItem());
				
				for(int item = 0; item < transactionItemSize ; item++) {
					int itemValue = Integer.parseInt(items[item]);
					if (itemValue > bitVector.getBiggestFrequentItem()) {
						break;
					}
					if(bitVector.isFrequentItem(itemValue)) {
						frequentItems.add(itemValue);
					}
				}
				int frequentItemsLength = frequentItems.size();
				for (int item1 = 0 ; item1 < frequentItemsLength ; item1++) {
					for (int item2 = item1+1 ; item2 < frequentItemsLength ; item2++) {
						int item1Value = frequentItems.get(item1);
						int item2Value = frequentItems.get(item2);
//						System.out.println("adding pair ( " + item1Value +" , " + item2Value + " ) ");
						if (bucketController.shouldAddCount(item1Value, item2Value)) {
							pairStorage.addCount(item1Value, item2Value);
						}
					}
				}
				
			}
			System.out.println("finished reading PassII transactions");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	public void readTransactions(PassIIResult passIIResult,IntItemCountStorage intItemCountStorage) throws FileNotFoundException {
		int numberOfCandidateItemsToMatch = 3 ;//TODO get this from the passResult;
		try {
			PassNResult passNResult = new PassNResult(passIIResult.getCandidateTriplets(), passIIResult.getExecutionTime(), numberOfCandidateItemsToMatch);
			passNResult.setTotalTransactionLines(passIIResult.getTotalTransactionLines());
//			passNResult.setUniqueCandidateFrequentItems(passIIResult.getUniqueCandidateTriplets());
			readTransactions(passNResult, intItemCountStorage);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void readTransactionsPassIII(PassIIResult passIIResult,IntItemCountStorage intItemCountStorage) throws FileNotFoundException {
		this.reader = createBufferedReader(getAbsolutefilePath());
		String line = null;
		String[] items = null;
		int numberOfCandidateItemsToMatch = 3;
		int transactionCount = 0;
		int matchedCountIndex = 0;
		try {
//			System.out.println("candidate items are " + passNResult.getUniqueCandidateFrequentItems());
			while ((line = reader.readLine()) != null) {
				items = line.split(FILE_DELIMITER);
				transactionCount++;
				//TODO add condition to skip candidateItem if the number of transactions has exceeded the threshlhold
				// If remaining transactions to process ( current transactionCount - TransactionTotal ) + current occurence count < support we can stop checking this candidate pair
				Iterator<IntTriplet> candidateItemsIt = passIIResult.getCandidateIntTriplets().iterator();
//				Iterator<IntItems> candidateItemsIt = passNResult.getUniqueCandidateFrequentItems().iterator();
				int candidateAnalized = 0; 
				int candidateSize = passIIResult.getCandidateIntTriplets().size();
//				int candidateSize = passNResult.getUniqueCandidateFrequentItems().size();
				TIntArrayList infrequentItems = new TIntArrayList();
//				System.out.println("Candidate size is " + candidateSize);
//				System.out.println("Transaction Line " + transactionCount);
				while(candidateItemsIt.hasNext() ||candidateAnalized < candidateSize) {
					matchedCountIndex = 0;
					IntTriplet candidateItems = candidateItemsIt.next();
					
					//check if threshold is met
//					int nbrLinesLeftToProcess = passNResult.getTotalTransactionLines() - transactionCount;
//					int threshold = nbrLinesLeftToProcess + intItemCountStorage.getCount(candidateItems);
////					System.out.println("total Transaction Lines "  + passNResult.getTotalTransactionLines());
////					System.out.println("Transaction Line " + transactionCount);
////					System.out.println("Threshold for " + candidateItems + " is " + threshold);
////					System.out.println("nrbr lines left ot process " + nbrLinesLeftToProcess);
//					if (threshold < passNResult.getSupport()) {
//						System.out.println("Removing candidate from analysis " +candidateItems);
//						candidateItemsIt.remove();
//						candidateSize--;
//						candidateAnalized++;
//					}
//					System.out.println("analizing candidateITem " + candidateItems);
					if(candidateItems.containsAtLeastOneItem(infrequentItems)) {
						candidateItemsIt.remove();
						candidateAnalized++;
						continue;
					}
					while (matchedCountIndex < numberOfCandidateItemsToMatch) {
//						System.out.println("analizy candidate " + candidateItems);
						int candidateItem = candidateItems.getItem(matchedCountIndex);
						int candidateItemIndex = Arrays.binarySearch(items, Integer.toString((candidateItem)));
						//System.out.println("candidateITemIndex after binary search is " + candidateItemIndex);
						if (candidateItemIndex <= -1) {
							infrequentItems.add(candidateItem);
							break ;
						} else {
							matchedCountIndex++;
							if (matchedCountIndex >= numberOfCandidateItemsToMatch) {
								//System.out.println("adding count for candidateItems" + candidateItems);
								intItemCountStorage.addCount(candidateItems);
								//System.out.println("getting count for candidateItems " + intItemCountStorage.getCount(candidateItems));
								break ;
							}
						}
					}
					candidateAnalized++;
				}
//				for (IntItems candidateItems : passNResult.getCandidateFrequentItems()) {
//					matchedCountIndex = 0;
//					finishedAnalyzingCandidateItem:
//					while (matchedCountIndex < numberOfCandidateItemsToMatch) {
//						System.out.println("analizy candidate " + candidateItems);
//						int candidateItem = candidateItems.getItem(matchedCountIndex);
//						int candidateItemIndex = Arrays.binarySearch(items, String.valueOf(candidateItem));
//						if (candidateItemIndex == -1) {
//							break finishedAnalyzingCandidateItem;
//						} else {
//							matchedCountIndex++;
//							if (matchedCountIndex >= numberOfCandidateItemsToMatch) {
//								intItemCountStorage.addCount(candidateItems);
//								break finishedAnalyzingCandidateItem;
//							}
//						}
//					}	
//				}
			}
//			System.out.println("Candidate storage count is " + intItemCountStorage);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void readTransactionsPassIIICharFileStorage(PassIIResult passIIResult,IntItemCountStorage intItemCountStorage) throws Exception {
		
		int numberOfFilesToRead = CandidateGenerator.getNumberOfFilesToRead();
		readTransactionsPassIIIChar(passIIResult,intItemCountStorage);
		for(int fileNumber = 0 ; fileNumber < numberOfFilesToRead ; fileNumber++) {
			List<CharTriplet> triplets = CandidateGenerator.readCandidateCharTripletFromDisk(fileNumber);
			passIIResult.setCandOptiCharTriplets(triplets);
			readTransactionsPassIIIChar(passIIResult,intItemCountStorage);
		} 
	}
	
	public void readTransactionsPassIIIChar(PassIIResult passIIResult,IntItemCountStorage intItemCountStorage) throws FileNotFoundException {
		this.reader = createBufferedReader(getAbsolutefilePath());
		String line = null;
		String[] items = null;
		int numberOfCandidateItemsToMatch = 3;
		int transactionCount = 0;
		int matchedCountIndex = 0;
		try {
//			System.out.println("candidate items are " + passNResult.getUniqueCandidateFrequentItems());
			while ((line = reader.readLine()) != null) {
				items = line.split(FILE_DELIMITER);
				transactionCount++;
				//TODO add condition to skip candidateItem if the number of transactions has exceeded the threshlhold
				// If remaining transactions to process ( current transactionCount - TransactionTotal ) + current occurence count < support we can stop checking this candidate pair
				Iterator<CharTriplet> candidateItemsIt = passIIResult.getCandOptiCharTriplets().iterator();
				int candidateAnalized = 0; 
				int candidateSize = passIIResult.getCandOptiCharTriplets().size();
//				int candidateSize = passNResult.getUniqueCandidateFrequentItems().size();
				TIntArrayList infrequentItems = new TIntArrayList();
//				TIntSet frequentItems = new TIntHashSet();
//				System.out.println("Candidate size is " + candidateSize);
//				System.out.println("Transaction Line " + transactionCount);
				while(candidateItemsIt.hasNext() ||candidateAnalized < candidateSize) {
					matchedCountIndex = 0;
					CharTriplet candidateItems = candidateItemsIt.next();
					
					//check if threshold is met
//					int nbrLinesLeftToProcess = passNResult.getTotalTransactionLines() - transactionCount;
//					int threshold = nbrLinesLeftToProcess + intItemCountStorage.getCount(candidateItems);
////					System.out.println("total Transaction Lines "  + passNResult.getTotalTransactionLines());
////					System.out.println("Transaction Line " + transactionCount);
////					System.out.println("Threshold for " + candidateItems + " is " + threshold);
////					System.out.println("nrbr lines left ot process " + nbrLinesLeftToProcess);
//					if (threshold < passNResult.getSupport()) {
//						System.out.println("Removing candidate from analysis " +candidateItems);
//						candidateItemsIt.remove();
//						candidateSize--;
//						candidateAnalized++;
//					}
//					System.out.println("analizing candidateITem " + candidateItems);
					if(candidateItems.containsAtLeastOneItem(infrequentItems)) {
						candidateItemsIt.remove();
						candidateAnalized++;
						continue;
					}
					
					while (matchedCountIndex < numberOfCandidateItemsToMatch) {
//						System.out.println("analizy candidate " + candidateItems);
						int candidateItem = candidateItems.getItem(matchedCountIndex);
						int candidateItemIndex = Arrays.binarySearch(items, Integer.toString((candidateItem)));
						//System.out.println("candidateITemIndex after binary search is " + candidateItemIndex);
						if (candidateItemIndex <= -1) {
							infrequentItems.add(candidateItem);
							break ;
						} else {
							matchedCountIndex++;
							if (matchedCountIndex >= numberOfCandidateItemsToMatch) {
								//System.out.println("adding count for candidateItems" + candidateItems);
								intItemCountStorage.addCount(candidateItems);
								//System.out.println("getting count for candidateItems " + intItemCountStorage.getCount(candidateItems));
								break ;
							}
						}
					}
					candidateAnalized++;
				}

			}
//			System.out.println("Candidate storage count is " + intItemCountStorage);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			passIIResult.setCandidateCharTriplets(null);
		}
	}
	

	
	public void readTransactions(PassNResult passNResult,IntItemCountStorage intItemCountStorage) throws FileNotFoundException {
		this.reader = createBufferedReader(getAbsolutefilePath());
		String line = null;
		String[] items = null;
		int numberOfCandidateItemsToMatch = passNResult.getPassNumber();
		int transactionCount = 0;
		int matchedCountIndex = 0;
		try {
//			System.out.println("candidate items are " + passNResult.getUniqueCandidateFrequentItems());
			while ((line = reader.readLine()) != null) {
				items = line.split(FILE_DELIMITER);
				transactionCount++;
				//TODO add condition to skip candidateItem if the number of transactions has exceeded the threshlhold
				// If remaining transactions to process ( current transactionCount - TransactionTotal ) + current occurence count < support we can stop checking this candidate pair
				Iterator<IntItems> candidateItemsIt = passNResult.getCandidateFrequentItems().iterator();
//				Iterator<IntItems> candidateItemsIt = passNResult.getUniqueCandidateFrequentItems().iterator();
				int candidateAnalized = 0; 
				int candidateSize = passNResult.getCandidateFrequentItems().size();
//				int candidateSize = passNResult.getUniqueCandidateFrequentItems().size();
				TIntArrayList infrequentItems = new TIntArrayList();
//				System.out.println("Candidate size is " + candidateSize);
//				System.out.println("Transaction Line " + transactionCount);
				while(candidateItemsIt.hasNext() ||candidateAnalized < candidateSize) {
					matchedCountIndex = 0;
					IntItems candidateItems = candidateItemsIt.next();
					
					if(candidateItems.containsAtLeastOneItem(infrequentItems)) {
						candidateItemsIt.remove();
						candidateAnalized++;
						continue;
					}
					while (matchedCountIndex < numberOfCandidateItemsToMatch) {
//						System.out.println("analizy candidate " + candidateItems);
						int candidateItem = candidateItems.getItem(matchedCountIndex);
						int candidateItemIndex = Arrays.binarySearch(items, Integer.toString((candidateItem)));
						//System.out.println("candidateITemIndex after binary search is " + candidateItemIndex);
						if (candidateItemIndex <= -1) {
							infrequentItems.add(candidateItem);
							break ;
						} else {
							matchedCountIndex++;
							if (matchedCountIndex >= numberOfCandidateItemsToMatch) {
								//System.out.println("adding count for candidateItems" + candidateItems);
								intItemCountStorage.addCount(candidateItems);
								//System.out.println("getting count for candidateItems " + intItemCountStorage.getCount(candidateItems));
								break ;
							}
						}
					}
					candidateAnalized++;
				}
			}
//			System.out.println("Candidate storage count is " + intItemCountStorage);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	
	public String getAbsolutefilePath() {
		return absolutefilePath;
	}

	public void setAbsolutefilePath(String absolutefilePath) {
		this.absolutefilePath = absolutefilePath;
	}

	public BufferedReader getReader() {
		return reader;
	}

	public void setReader(BufferedReader reader) {
		this.reader = reader;
	}

}
